package com.onec.service.gateway.security;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.onec.service.api.content.AuthConstants;
import com.onec.service.api.content.FilterContent;
import com.onec.service.gateway.enums.SourceEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Optional;

/**
 * 鉴权管理器
 *
 * @author onec
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private static final Logger logger = LoggerFactory.getLogger(AuthorizationManager.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        String path = request.getURI().getPath();
        //PathMatcher pathMatcher = new AntPathMatcher();
        logger.info("TOKEN 验证：请求访问方法：{},请求地址：{}", request.getMethod(), path);
        // 对应跨域的预检请求直接放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }

        // GET 直接放行
        if (request.getMethod() == HttpMethod.GET) {
            return Mono.just(new AuthorizationDecision(true));
        }
        // 如果是agent 访问直接放行：
        String source = request.getHeaders().getFirst(FilterContent.AUTHORIZE_SOURCE);
        if (SourceEnum.isAgent(source)) {
            return Mono.just(new AuthorizationDecision(true));
        }
//        if (!pathMatcher.match(AuthConstants.ADMIN_URL_PATTERN, path)) {
//
//        }

        // token为空拒绝访问
        String token = request.getHeaders().getFirst(AuthConstants.JWT_TOKEN_HEADER);
        if (StrUtil.isBlank(token)) {
            return Mono.just(new AuthorizationDecision(false));
        }
        // 从缓存取资源权限角色关系列表
        Object object = redisTemplate.opsForHash().get(AuthConstants.RESOURCE_ROLES_KEY, path);
        logger.info("该资源：path:{},是否不需要权限：{}", path, Optional.ofNullable(object).isEmpty());
        if (Optional.ofNullable(object).isEmpty()) {
            return Mono.just(new AuthorizationDecision(true));
        }
        List<String> authorities = Convert.toList(String.class, object);
        return mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(roleId -> {
                    // roleId是请求用户的角色(格式:ROLE_{roleId})，authorities是请求资源所需要角色的集合
                    logger.info("访问路径：{}", path);
                    logger.info("用户角色信息：{}", roleId);
                    logger.info("资源需要权限authorities：{}", authorities);
                    return authorities.contains(roleId);
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }


}
